import { AssetManager, assetManager } from 'cc';

export type ReleaseCallback = ()=>void;

export interface IAssetsBundleManager {
    getDependencies(name: string): string[];

    getAssetsBundle(name: string): AssetsBundle|null;
}

export default class AssetsBundle {
    private _manager: IAssetsBundleManager;
    private _bundle: AssetManager.Bundle;
    private _ref: number;
    private _depedBy: number = 0;
    private _autoRelease: boolean;

    private _onRelease: ReleaseCallback[];

    get Bundle(): AssetManager.Bundle {
        return this._bundle;
    }

    public get canBeRelease(): boolean {
        if (!this._autoRelease) return false;
        return this._ref === 1 + this._depedBy;
    }

    constructor(manager: IAssetsBundleManager, bundle: AssetManager.Bundle) {
        this._manager = manager;
        this._bundle = bundle;
        this._ref = 0;
        this._autoRelease = true;
        this.onLoad();
    }

    getBundle(): AssetManager.Bundle {
        return this._bundle;
    }

    onReleaseCallback(callback: ReleaseCallback): void {
        if (!this._onRelease) this._onRelease = [];
        this._onRelease.push(callback);
    }

    offReleaseCallback(callback: ReleaseCallback): void {
        if (!this._onRelease) return;
        const index: number = this._onRelease.indexOf(callback);
        if (index === -1) return;
        this._onRelease.splice(index, 1);
    }

    setAutoRelease(auto: boolean): void {
        this._autoRelease = auto ?? false;
        if (this._autoRelease && this._ref <= 0) {
            this.releaseInternal();
        }
    }

    addRef(): void {
        this._ref++;
    }

    release(): boolean {
        this._ref--;
        if (!this._autoRelease) return false;
        if (this._ref <= 0) {
            this.releaseInternal();
            return true;
        }
        return false;
    }

    private releaseInternal(): void {
        console.debug("release bundle: ", this._bundle.name);
        this._bundle.releaseAll();
        assetManager.removeBundle(this._bundle);
        this.onRelease();
        if (this._onRelease) {
            for (const callback of this._onRelease) {
                callback();
            }
            this._onRelease.length = 0;
        }
    }

    private onLoad(): void {
        // 不考虑循环引用的问题，循环引用时，构建会失败
        // 加载顺序出错时这里会出错，LoaderManager中已处理加载顺序
        const manager = this._manager;
        for (const dep of manager.getDependencies(this._bundle.name)) {
            const depBundle = manager.getAssetsBundle(dep);
            if (depBundle) {
                depBundle.addRef();
                depBundle._depedBy++;
            } else {
                console.error(`Bundle ${dep} has not been loaded!`);
            }
        }

    }

    private onRelease(): void {
        const manager = this._manager;
        for (const dep of manager.getDependencies(this._bundle.name)) {
            const depBundle = manager.getAssetsBundle(dep);
            if (depBundle) {
                depBundle._depedBy--;
                depBundle.release();
            } else {
                console.error(`Bundle ${dep} has not been loaded!`);
            }
        }
    }

    
}